/*
This software is subject to the license described in the license.txt file
included with this software distribution. You may not use this file except in compliance
with this license.

Copyright (c) Dynastream Innovations Inc. 2015
All rights reserved.
*/

#include <string.h>
#include "message_cache.h"
#include "nordic_common.h"

static bool mc_pop(message_cache_t * p_mc);
static bool mc_compare(uint8_t * p_msg, mesh_message_t * p_opt_msg);

void mc_clear(message_cache_t * p_mc)
{
    if (p_mc == NULL)
    {
        return;
    }

    p_mc->back  = 0;
    p_mc->front = 0;
    p_mc->count = 0;
}

void mc_add(message_cache_t * p_mc, uint8_t * p_message)
{
    // If the cache is full, delete an old element to make room for the new one
    if (p_mc->count >= p_mc->size)
    {
        UNUSED_PARAMETER(mc_pop(p_mc));
    }

    // Copy new element into the queue
    // Convert from ANT message format into optimized struct
    p_mc->buffer[p_mc->back].page  = p_message[0];
    p_mc->buffer[p_mc->back].dst   = p_message[1];
    p_mc->buffer[p_mc->back].seq   = p_message[6];
    p_mc->buffer[p_mc->back].data  = p_message[7];
    p_mc->buffer[p_mc->back].ticks = 0;

    p_mc->back = (p_mc->back == (p_mc->size - 1)) ? 0 : p_mc->back + 1;
    p_mc->count++;
}


bool mc_is_in_cache(message_cache_t * p_mc, uint8_t * p_message)
{
    if (p_mc->count == 0)
    {
        return false;
    }

    uint8_t iter = p_mc->front;

    for (int i = 0; i < p_mc->count; i++)
    {
        if (mc_compare(p_message, &p_mc->buffer[iter]))
        {
            return true;
        }

        iter = (iter == (p_mc->size - 1)) ? 0 : iter + 1;
    }
    return false;
}


void mc_cleanup(message_cache_t * p_mc, uint8_t max_ticks)
{
    if (p_mc->count == 0)
    {
        return;
    }

    uint8_t size        = p_mc->count;
    uint8_t iter        = p_mc->front;
    bool    msg_expired = false;

    for (int i = 0; i < size; i++)
    {
        p_mc->buffer[iter].ticks++;

        if (p_mc->buffer[iter].ticks >= max_ticks)
        {
            msg_expired = true;
        }

        iter = (iter == (p_mc->size - 1)) ? 0 : iter + 1;

        if (msg_expired)
        {
            p_mc->front = iter;
            p_mc->count--;
        }
    }
}


/**@brief Pop message from cache
 *
 * @param p_mc          Pointer to message cache struct
 *
 * @return  true if able to pop item
 */
static bool mc_pop(message_cache_t * p_mc)
{
    if (p_mc->count == 0)
    {
        return false;
    }

    p_mc->front = (p_mc->front == (p_mc->size - 1)) ? 0 : p_mc->front + 1;
    p_mc->count--;
    return true;
}


/**@brief Compare a raw ANT message with optimized messagestruct
 * @param p_msg         Pointer to raw ANT message
 * @param p_opt_msg     Pointer to optimized message struct
 * @return true if the values match, false otherwise
 */
static bool mc_compare(uint8_t * p_msg, mesh_message_t * p_opt_msg)
{
    bool result = true;

    if (p_opt_msg->page != p_msg[0])
    {
        result = false;
    }

    if (p_opt_msg->dst != p_msg[1])
    {
        result = false;
    }

    if (p_opt_msg->seq != p_msg[6])
    {
        result = false;
    }

    if (p_opt_msg->data != p_msg[7])
    {
        result = false;
    }

    return result;
}


